﻿using MiniFox.Components;
using MiniFox.Infrastructure;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace MiniFox.Data
{
    /// <summary>
    /// 
    /// </summary>
    public abstract class DataProviderBase : Component, IDataProvider
    {
        private DataSourceFactory _factory;

        #region constructor

        /// <summary>
        /// 
        /// </summary>
        protected DataProviderBase()
        {
        }
        #endregion
        
        /// <summary>
        /// 
        /// </summary>
        public DataSourceFactory DataSourceFactory
        {
            get 
            { 
                if (_factory == null)
                {
                    this._factory = CreateDataSourceFactory();
                }
                return _factory;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        protected virtual DataSourceFactory CreateDataSourceFactory()
        {
            DataSourceFactory factory = new DataSourceFactory();
            return factory;
        }

        #region 事件观察
        /// <summary>
        /// 
        /// </summary>
        public event EventHandler<DataOperatingEventArgs> DataBatchExecuting;
        /// <summary>
        /// 
        /// </summary>
        public event EventHandler<DataOperatedEventArgs> DataBatchExecuted;
        /// <summary>
        /// 
        /// </summary>
        public event EventHandler<DataOperatingEventArgs> DataInserting;
        /// <summary>
        /// 
        /// </summary>
        public event EventHandler<DataOperatedEventArgs> DataInserted;
        /// <summary>
        /// 
        /// </summary>
        public event EventHandler<DataOperatingEventArgs> DataUpdating;
        /// <summary>
        /// 
        /// </summary>
        public event EventHandler<DataOperatedEventArgs> DataUpdated;
        /// <summary>
        /// 
        /// </summary>
        public event EventHandler<DataOperatingEventArgs> DataDeleting;
        /// <summary>
        /// 
        /// </summary>
        public event EventHandler<DataOperatedEventArgs> DataDeleted;
        /// <summary>
        /// 
        /// </summary>
        public event EventHandler<DataLoadedEventArgs> DataLoaded;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="data"></param>
        protected virtual void OnDataBatchExecuting(params object[] data)
        {
            this.DataBatchExecuting?.Invoke(this, new DataOperatingEventArgs(data));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="success"></param>
        /// <param name="data"></param>
        protected virtual void OnDataBatchExecuted(bool success, params object[] data)
        {
            this.DataBatchExecuted?.Invoke(this, new DataOperatedEventArgs(success, data));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="data"></param>
        protected virtual void OnDataInserting(params object[] data)
        {
            this.DataInserting?.Invoke(this, new DataOperatingEventArgs(data));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="success"></param>
        /// <param name="data"></param>
        protected virtual void OnDataInserted(bool success, params object[] data)
        {
            this.DataInserted?.Invoke(this, new DataOperatedEventArgs(success, data));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="data"></param>
        protected virtual void OnDataUpdating(params object[] data)
        {
            this.DataUpdating?.Invoke(this, new DataOperatingEventArgs(data));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="success"></param>
        /// <param name="data"></param>
        protected virtual void OnDataUpdated(bool success, params object[] data)
        {
            this.DataUpdated?.Invoke(this, new DataOperatedEventArgs(success, data));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="data"></param>
        protected virtual void OnDataDeleting(params object[] data)
        {
            this.DataDeleting?.Invoke(this, new DataOperatingEventArgs(data));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="success"></param>
        /// <param name="data"></param>
        protected virtual void OnDataDeleted(bool success, params object[] data)
        {
            this.DataDeleted?.Invoke(this, new DataOperatedEventArgs(success, data));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="data"></param>
        protected virtual void OnDataLoaded(object data)
        {
            this.DataLoaded?.Invoke(this, new DataLoadedEventArgs(data));
        }
        #endregion

        #region Create、Update、Delete
        /// <summary>
        /// 将实体插入到数据库中对应的表中
        /// </summary>
        /// <param name="entities">包含数据的实体</param>
        /// <returns>操作是否成功</returns>
        public abstract int Insert<TEntity>(params TEntity[] entities) where TEntity : class;

        /// <summary>
        /// 修改数据库中对应表的数据
        /// </summary>
        /// <param name="entities">包含数据的实体</param>
        /// <returns>操作是否成功</returns>
        public abstract int Update<TEntity>(params TEntity[] entities) where TEntity : class;
        /// <summary>
        /// 删除数据库中对应表的数据
        /// </summary>
        /// <param name="entities">包含数据的相关实体</param>
        /// <returns>操作是否成功</returns>
        public abstract int Delete<TEntity>(params TEntity[] entities) where TEntity : class;

        #endregion

        #region  GetDataCommand

        /// <summary>
        /// 获取命令对象
        /// </summary>
        /// <param name="name">命令名</param>
        /// <returns></returns>
        public IDataCommand GetDataCommand(string name)
        {
            return this.DataSourceFactory.GetDataCommand(name);
        }
        #endregion

        #region 获取查询命令对象
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <returns></returns>
        public abstract IDataQuery<TEntity> GetDataQuery<TEntity>() where TEntity : class;
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <returns></returns>
        public abstract IObjectQuery<TEntity> GetObjectQuery<TEntity>() where TEntity : class;
        #endregion

        #region OperationExecute
        /// <summary>
        /// 多条数据批量执行操作
        /// </summary>
        /// <param name="name">命令名</param>
        /// <param name="entities">多个实体条目</param>
        /// <returns>执行成功的条数</returns>
        public int OperationExecute(string name, params object[] entities)
        {
            var dataOperation = this.GetDataCommand(name);
            return dataOperation.ExecuteOperation(entities);
        }
        #endregion

        #region QueryExecute
        /// <summary>
        /// 
        /// </summary>
        /// <param name="name">命令名</param>
        /// <param name="request"></param>
        /// <returns></returns>
        public ResultSet QueryExecute(string name, IQueryObject request)
        {
            return QueryExecute(name, request.GetParametersDictionary());
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <param name="parameters">参数</param>
        /// <returns></returns>
        public ResultSet QueryExecute(string name, object parameters)
        {
            var query = GetDataCommand(name);
            ResultSet result = query.ExecuteDataQuery(parameters);
            this.OnDataLoaded(result.Data);
            return result;
        }
        #endregion

        #region QueryExecute~T
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="request"></param>
        /// <returns></returns>
        public ResultSet<TEntity> QueryExecute<TEntity>(IQueryObject request) where TEntity : class
        {
            IDataQuery<TEntity> query = GetDataQuery<TEntity>();
            ResultSet<TEntity> result;
            if (request != null)
            {
                var lambda = request.GetQueryExpression<TEntity>();
                result = query.Execute(lambda, request.SortFields);
            }
            else
            {
                result = query.Execute(null, null);
            }
            return result;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="expression"></param>
        /// <param name="sortFields"></param>
        /// <returns></returns>
        public ResultSet<TEntity> QueryExecute<TEntity>(Expression<Func<TEntity, bool>> expression, IDictionary<string, bool> sortFields) where TEntity : class
        {
            IDataQuery<TEntity> query = GetDataQuery<TEntity>();
            ResultSet<TEntity> result = query.Execute(expression, sortFields);
            this.OnDataLoaded(result.Data);
            return result;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="expression"></param>
        /// <param name="sortFields"></param>
        /// <param name="maxRowCount"></param>
        /// <returns></returns>
        public ResultSet<TEntity> QueryExecute<TEntity>(Expression<Func<TEntity, bool>> expression, IDictionary<string, bool> sortFields, int? maxRowCount) where TEntity : class
        {
            IDataQuery<TEntity> query = GetDataQuery<TEntity>();
            ResultSet<TEntity> result = query.Execute(expression, sortFields, maxRowCount);
            this.OnDataLoaded(result.Data);
            return result;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="queryable"></param>
        /// <param name="request"></param>
        /// <returns></returns>
        public virtual ResultSet<TEntity> QueryExecute<TEntity>(IQueryable<TEntity> queryable, IQueryObject request) where TEntity : class
        {
            IDataQuery<TEntity> query = GetDataQuery<TEntity>();
            ResultSet<TEntity> result;
            if (request != null)
            {
                var lambda = request.GetQueryExpression<TEntity>();
                result = QueryExecute(queryable, lambda, request.SortFields);
            }
            else
            {
                result = QueryExecute(queryable, null, null);
            }
            return result;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="queryable"></param>
        /// <param name="expression"></param>
        /// <param name="sortFields"></param>
        /// <returns></returns>
        public ResultSet<TEntity> QueryExecute<TEntity>(IQueryable<TEntity> queryable, Expression<Func<TEntity, bool>> expression, IDictionary<string, bool> sortFields) where TEntity : class
        {
            IDataQuery<TEntity> query = GetDataQuery<TEntity>();
            var result = query.Execute(queryable, expression, sortFields);
            this.OnDataLoaded(result.Data);
            return result;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="queryable"></param>
        /// <param name="expression"></param>
        /// <param name="sortFields"></param>
        /// <param name="maxRowCount"></param>
        /// <returns></returns>
        public ResultSet<TEntity> QueryExecute<TEntity>(IQueryable<TEntity> queryable, Expression<Func<TEntity, bool>> expression, IDictionary<string, bool> sortFields, int? maxRowCount) where TEntity : class
        {
            IDataQuery<TEntity> query = GetDataQuery<TEntity>();
            var result = query.Execute(queryable, expression, sortFields, maxRowCount);
            this.OnDataLoaded(result.Data);
            return result;
        }
        #endregion

        #region PagedQueryExecute
        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <param name="request"></param>
        /// <returns></returns>
        public PagedResultSet PagedQueryExecute(string name, IPagedQueryObject request)
        {
            return PagedQueryExecute(name, request.GetParametersDictionary(), request.PageIndex, request.PageSize, request.SortFields);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <param name="parameters"></param>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <param name="sortFields"></param>
        /// <returns></returns>
        public PagedResultSet PagedQueryExecute(string name, object parameters, int pageIndex, int pageSize, IDictionary<string, bool> sortFields)
        {
            var query = GetDataCommand(name);
            var result = query.ExecuteDataQuery(pageIndex, pageSize, sortFields, parameters);
            this.OnDataLoaded(result.Data);
            return result;
        }
        #endregion

        #region PagedQueryExecute~T
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="request"></param>
        /// <returns></returns>
        public PagedResultSet<TEntity> PagedQueryExecute<TEntity>(IPagedQueryObject request) where TEntity : class
        {
            return this.PagedQueryExecute(request.GetQueryExpression<TEntity>(), request.PageIndex, request.PageSize, request.SortFields);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="expression"></param>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <param name="sortFields"></param>
        /// <returns></returns>
        public PagedResultSet<TEntity> PagedQueryExecute<TEntity>(Expression<Func<TEntity, bool>> expression, int pageIndex, int pageSize, IDictionary<string, bool> sortFields) where TEntity : class
        {
            IDataQuery<TEntity> query = GetDataQuery<TEntity>();
            var result = query.Execute(expression, sortFields, pageIndex, pageSize);
            this.OnDataLoaded(result.Data);
            return result;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="queryable"></param>
        /// <param name="request"></param>
        /// <returns></returns>
        public PagedResultSet<TEntity> PagedQueryExecute<TEntity>(IQueryable<TEntity> queryable, IPagedQueryObject request) where TEntity : class
        {
            return this.PagedQueryExecute(queryable, request.GetQueryExpression<TEntity>(), request.PageIndex, request.PageSize, request.SortFields);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="queryable"></param>
        /// <param name="expression"></param>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <param name="sortFields"></param>
        /// <returns></returns>
        public PagedResultSet<TEntity> PagedQueryExecute<TEntity>(IQueryable<TEntity> queryable, Expression<Func<TEntity, bool>> expression, int pageIndex, int pageSize, IDictionary<string, bool> sortFields) where TEntity : class
        {
            IDataQuery<TEntity> query = GetDataQuery<TEntity>();
            var result = query.Execute(queryable, expression, sortFields, pageIndex, pageSize);
            this.OnDataLoaded(result.Data);
            return result;
        }
        #endregion

        #region ListupExecute~T
        ///// <summary>
        ///// 
        ///// </summary>
        ///// <typeparam name="TEntity"></typeparam>
        ///// <param name="request"></param>
        ///// <returns></returns>
        //public DataResultSet<TEntity> ListupExecute<TEntity>(ListupRequest request) where TEntity : class
        //{
        //    IDataQuery<TEntity> query = GetDataQuery<TEntity>();
        //    DataResultSet<TEntity> result;
        //    if (request != null)
        //    {
        //        var lambda = request.GetQueryExpression<TEntity>();
        //        result = query.Execute(lambda, request.SortFields, request.MaxCount);
        //    }
        //    else
        //    {
        //        result = query.Execute(null, null, 10);
        //    }
        //    return result;
        //}
        ///// <summary>
        ///// 
        ///// </summary>
        ///// <typeparam name="TEntity"></typeparam>
        ///// <param name="queryable"></param>
        ///// <param name="request"></param>
        ///// <returns></returns>
        //public DataResultSet<TEntity> ListupExecute<TEntity>(IQueryable<TEntity> queryable, ListupRequest request) where TEntity : class
        //{
        //    IDataQuery<TEntity> query = GetDataQuery<TEntity>();
        //    DataResultSet<TEntity> result;
        //    if (request != null)
        //    {
        //        var lambda = request.GetQueryExpression<TEntity>();
        //        result = query.Execute(queryable, lambda, request.SortFields, request.MaxCount);
        //    }
        //    else
        //    {
        //        result = query.Execute(queryable, null, null, 10);
        //    }
        //    return result;
        //}
        #endregion

        #region TreeQueryExecute
        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <param name="request"></param>
        /// <returns></returns>
        public TreeResultObject TreeQueryExecute(string name, TreeQueryObject request)
        {
            var resultSet = this.QueryExecute(name, request);
            var resultObject = new TreeResultObject(resultSet.Data, request.NodeKey, request.SuperKey, request.SortKey, request.GroupKey);
            return resultObject;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="request"></param>
        /// <returns></returns>
        public TreeResultObject<TEntity> TreeQueryExecute<TEntity>(TreeQueryObject request) where TEntity : class
        {
            var resultSet = this.QueryExecute<TEntity>(request);
            var resultObject = new TreeResultObject<TEntity>(resultSet.Data, request.NodeKey, request.SuperKey, request.SortKey, request.GroupKey);
            return resultObject;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="queryable"></param>
        /// <param name="request"></param>
        /// <returns></returns>
        public TreeResultObject<TEntity> TreeQueryExecute<TEntity>(IQueryable<TEntity> queryable, TreeQueryObject request) where TEntity : class
        {
            var resultSet = this.QueryExecute<TEntity>(queryable, request);
            var resultObject = new TreeResultObject<TEntity>(resultSet.Data, request.NodeKey, request.SuperKey, request.SortKey, request.GroupKey);
            return resultObject;
        }
        #endregion

    }
}
